package bpd;

import static robocode.util.Utils.normalRelativeAngleDegrees;
import java.awt.Color;
import robocode.Robot;
import robocode.BulletMissedEvent;
import robocode.HitByBulletEvent;
import robocode.HitWallEvent;
import robocode.HitRobotEvent;
import robocode.ScannedRobotEvent;

/**
 * The PanzerBot class creates a robot that will attempt to beat eight of the sample robots that
 * are included in the Robocode installation package (Corners, Crazy, Fire, RamFire, SittingDuck,
 * SpinBot, Tracker, and Walls).
 * 
 * @author BJ Peter DeLaCruz
 * @version 1.1
 * 
 */
public class PanzerBot extends Robot {

  /** An enemy's energy level. */
  protected double enemyEnergy;
  /** Change in energy level of an enemy. */
  protected double dEnemyEnergy;
  /** An enemy's bearing. */
  protected double enemyBearing;
  /** Number of bullets that missed the enemy. */
  protected int numberBulletsMissed = 0;

  /**
   * Creates a robot that will rotate its body constantly.
   */
  public void run() {
    
    // Make the robot green.
    this.setColors(new Color(0, 110, 0), new Color(0, 110, 0), new Color(0, 110, 0));
    
    this.enemyEnergy = 100.0;
    this.dEnemyEnergy = 0.0;

    while (true) {
      turnRight(10.0);
    }

  }

  /**
   * Makes the robot either evade an enemy's bullet or stand and fight the enemy. Either way, the
   * robot will fire at the enemy if the enemy is in front of it and at a certain distance; out of
   * energy, or sitting still.
   * 
   * @param e Contains information about the enemy, e.g. its location
   */
  public void onScannedRobot(ScannedRobotEvent e) {

    this.enemyBearing = e.getBearing();

    // Fire at the enemy when it is out of energy.
    if (e.getEnergy() == 0.0) {
      fire(3);
    }
    
    // Fire at the enemy when it is in front of the robot and sitting still...
    if (e.getBearing() >= -5.0 && e.getBearing() <= 5.0 && e.getVelocity() == 0.0) {
      fire(3);
    }
    // ...and/or very close.
    if (e.getBearing() >= -5.0 && e.getBearing() <= 5.0 && e.getDistance() <= 250.0) {
      fire(3);
    }
    
    // Detect a change in the enemy's energy level by subtracting its current energy level from
    // enemyEnergy, which holds the enemy's energy level from the previous call to this method. The
    // change in energy level signifies that the enemy fired a bullet at the robot.
    this.dEnemyEnergy = this.enemyEnergy - e.getEnergy();
    
    // If the enemy fired a bullet, make the robot evade it by turning perpendicular to the bullet
    // and moving forward a little bit.
    if (this.dEnemyEnergy != 0.0 && this.dEnemyEnergy <= 3.0) {
      turnLeft(90.0);
      ahead(50);
    }

    // Turn towards the enemy.
    turnRight(normalRelativeAngleDegrees(e.getBearing()));
    
    // Get the enemy's current energy level and use it in the next call to this method.
    this.enemyEnergy = e.getEnergy();

    scan();

  }

  /**
   * Makes the robot fire at the enemy only if the enemy is stationary and the robot got hit by one
   * of its bullets.
   * 
   * @param e Contains information about the bullet that just hit the robot, e.g. its bearing
   */
  public void onHitByBullet(HitByBulletEvent e) {
    
    // Keep track of the number of bullets that missed the enemy. If a bullet missed, then the
    // enemy that was stationary now moved.
    this.numberBulletsMissed = 0;
    
    // Fire at the enemy only if the enemy is sitting still, i.e. the bullet's bearing is the same
    // as that of the enemy. The enemy's bearing was read in from the onScannedRobot method.
    if (Math.floor(e.getBearing()) == Math.floor(this.enemyBearing)) {
      while (e.getBearing() >= -5.0 && e.getBearing() <= 5.0 &&
             (this.enemyBearing >= -5.0 && this.enemyBearing <= 5.0)) {
        fire(3);
        // If the enemy that was stationary moved, cease firing.
        if (this.numberBulletsMissed != 0) {
          break;
        } // end if
      } // end while
    } // end if

  }

  /**
   * Keeps track of the number of times that the robot missed the enemy.
   * 
   * @param e Contains information about the bullet that missed the enemy
   */
  public void onBulletMissed(BulletMissedEvent e) {
    this.numberBulletsMissed++;
  }

  /**
   * Makes the robot turn towards the enemy and fire at it.
   * 
   * @param e Contains information about the enemy that bumped into the robot, e.g. its bearing
   */
  public void onHitRobot(HitRobotEvent e) {

    // Keep track of the number of bullets that missed the enemy. If a bullet missed, then the
    // enemy that bumped into the robot now moved.
    this.numberBulletsMissed = 0;
    
    // Turn towards the enemy.
    turnRight(normalRelativeAngleDegrees(e.getBearing()));
    
    while (e.getBearing() >= -5.0 && e.getBearing() <= 5.0) {
      fire(3);
      // If the enemy that bumped into the robot moved, cease firing.
      if (this.numberBulletsMissed != 0) {
        break;
      } // end if
    } // end while

  }
  
  /**
   * Calls the moveToCenter method to make the robot move to the center of the battlefield if it
   * hit a wall.
   * 
   * @param e Contains information about the wall that the robot just hit, e.g. its bearing
   */
  public void onHitWall(HitWallEvent e) {
    moveToCenter();
  }

  /**
   * Makes the robot move to the center of the battlefield.
   */
  protected void moveToCenter() {

    // Get the coordinates for the center of the battlefield.
    double centerY = getBattleFieldHeight() / 2.0;
    double centerX = getBattleFieldWidth() / 2.0;

    // Make the robot face one direction initially.
    turnLeft(this.getHeading() % 360.0);

    // If the robot is above the center of the battlefield, make it do an about face (turn around
    // 180 degrees).
    if (this.getY() > centerY) {
      turnLeft(180.0);
    }

    // Move to the same y-coordinate as the center of the battlefield.
    ahead(Math.abs(this.getY() - centerY));

    // Figure out which way to turn.
    if (this.getHeading() == 0.0 && this.getX() < centerX) {
      turnRight(90.0);
    }
    else if (this.getHeading() == 180.0 && this.getX() < centerX) {
      turnLeft(90.0);
    }
    else if (this.getHeading() == 0.0 && this.getX() > centerX) {
      turnLeft(90.0);
    }
    else if (this.getHeading() == 180.0 && this.getX() > centerX) {
      turnRight(90.0);
    }

    // Now move to the center of the battlefield.
    ahead(Math.abs(this.getX() - centerX));

  }

}
